﻿using System;
using System.Linq;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.Windows.Media.Imaging;
using Microsoft.WebMatrix.Extensibility;
using Microsoft.WebMatrix.Extensibility.Editor;
using System.Collections.Generic;

using Autobiography.Helpers;

namespace Autobiography
{
    /// <summary>
    /// A sample WebMatrix extension.
    /// </summary>
    [Export(typeof(Extension))]
    public class Autobiography : Extension
    {
        /// <summary>
        /// Imports the FileTableService interface to gain access to save events
        /// </summary>
        [Import(typeof(IFileTableService))]
        private IFileTableService FileTableService { get; set; }

        // TODO: Add icons for other menu choices. Maybe an overlay plus for the Entire Site Backup, a DB icon for the Manage History...

        /// <summary>
        /// Stores a reference to the small star image.
        /// </summary>
        private readonly BitmapImage _historyImageSmall = new BitmapImage(new Uri("pack://application:,,,/Autobiography;component/history_16.png", UriKind.Absolute));

        /// <summary>
        /// Stores a reference to the large star image.
        /// </summary>
        private readonly BitmapImage _historyImageLarge = new BitmapImage(new Uri("pack://application:,,,/Autobiography;component/history_32.png", UriKind.Absolute));

        /// <summary>
        /// Stores a reference to the WebMatrix host interface.
        /// </summary>
        private IWebMatrixHost _webMatrixHost;

        /// <summary>
        /// Stores a reference to the EntityFramework context
        /// </summary>
        private Model.Context _ctx;

        /// <summary>
        /// Initializes a new instance of the Autobiography class.
        /// </summary>
        public Autobiography() : base("Autobiography") { }

        /// <summary>
        /// Called to initialize the extension.
        /// </summary>
        /// <param name="host">WebMatrix host interface.</param>
        /// <param name="initData">Extension initialization data.</param>
        protected override void Initialize(IWebMatrixHost host, ExtensionInitData initData)
        {
            System.Data.Entity.Database.DefaultConnectionFactory = new System.Data.Entity.Infrastructure.SqlCeConnectionFactory("System.Data.SqlServerCe.4.0");
            System.Data.Entity.Database.SetInitializer<Model.Context>(new System.Data.Entity.DropCreateDatabaseIfModelChanges<Model.Context>());
            //System.Data.Entity.Database.SetInitializer<Model.Context>(new System.Data.Entity.CreateDatabaseIfNotExists<Model.Context>());

            var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
            System.IO.Directory.CreateDirectory(System.IO.Path.Combine(localAppData, "WebMatrix Autobiography"));
            var db = System.IO.Path.Combine(localAppData, "WebMatrix Autobiography", "autobiography.sdf");
            _ctx = new Model.Context(@"Data Source=" + db);

            FileTableService.EditorOpened += new EventHandler<EditorEventArgs>(FileTableService_EditorOpened);
            FileTableService.PostSave += (s, e) =>
            {
                // Use background task to save to prevent any blocking on the main UI thread
                // if this fails I should use:
                // var editor = e.Editor.ServiceProvider.GetService<IEditorText>() as IEditorText;
                // editor.Text, but what should I get the bytes with - probably UTF8?
                string FilePath = e.Editor.FilePath;
                System.Threading.Tasks.Task.Factory.StartNew(() =>
                {
                    AddFileHistory(FilePath);
                });
            };

            // Add a simple button to the Ribbon
            var button = new RibbonGroup(
                "History",
                new [] { new RibbonSplitButton(
                    "History",
                    new DelegateCommand(HandleRibbonButtonInvoke),
                    null,
                    new [] {
                        new RibbonButton(
                            "History",
                            new DelegateCommand(HandleRibbonButtonInvoke),
                            null,
                            _historyImageSmall,
                            _historyImageLarge
                        )
                        //new RibbonButton(
                        //    "Manage History",
                        //    new DelegateCommand(HandleRibbonButtonInvoke),
                        //    null,
                        //    _historyImageSmall,
                        //    _historyImageLarge
                        //),
                        ,new RibbonButton(
                            "Add Entire Site History",
                            new DelegateCommand(SnapshotEntireSite),
                            null,
                            _historyImageSmall,
                            _historyImageLarge
                        )
                    },
                    _historyImageSmall,
                    _historyImageLarge
            )});
            initData.RibbonItems.Add(button);

            _webMatrixHost = host;
            _webMatrixHost.WorkspaceChanged += (s, e) =>
            {
                button.IsVisible = e.NewWorkspace is IEditorWorkspace;
            };
        }

        void FileTableService_EditorOpened(object sender, EditorEventArgs e)
        {
            var a = sender as IEditor;
            var s = e.Editor.FilePath;
            if (_ctx != null)
            {
                DateTime modified = System.IO.File.GetLastWriteTime(e.Editor.FilePath);
                int count = _ctx.Files.Where(f => f.FullPath == e.Editor.FilePath && f.Modified == modified).Count();
                if (count == 0)
                {
                    // store this file in the repository as the first revision
                    AddFileHistory(e.Editor.FilePath);
                }
            }
        }

        void AddFileHistory(string FilePath)
        {
            byte[] bytes = null;
            using (var fs = System.IO.File.OpenRead(FilePath))
            {
                bytes = fs.Compress();
            }

            DateTime modified = System.IO.File.GetLastWriteTime(FilePath);
            lock (_ctx)
            {
                _ctx.Files.Add(new Model.FileEntry
                {
                    FullPath = FilePath,
                    Modified = modified,
                    Stream = bytes
                });
                try
                {
                    _ctx.SaveChanges();
                }
                catch (System.Data.Entity.Validation.DbEntityValidationException ex)
                {
                    foreach (var validationErrors in ex.EntityValidationErrors)
                    {
                        foreach (var validationError in validationErrors.ValidationErrors)
                        {
                            Trace.TraceInformation("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
                        }
                    }
                    ex = null;
                }
            }
        }

        /// <summary>
        /// Called when the Ribbon button is invoked.
        /// </summary>
        /// <param name="parameter">Unused.</param>
        private void HandleRibbonButtonInvoke(object parameter)
        {
            var ws = (_webMatrixHost.Workspace as IEditorWorkspace);
            if (ws != null && ws.CurrentEditor != null && ws.CurrentEditor.FilePath != null)
            {
                var files = _ctx.Files.Where(f => f.FullPath == ws.CurrentEditor.FilePath).ToList();
                if (files.Count() > 0)
                {
                    var p = new UI.History(files);
                    var title = ws.CurrentEditor.FilePath.Replace(_webMatrixHost.WebSite.Path,"");
                    var result = _webMatrixHost.ShowDialog(title, p, DialogSize.Medium).GetValueOrDefault();
                    if (result && p.SelectedItem != null)
                    {
                        var file = p.SelectedItem as Model.FileEntry;
                        if (file != null)
                        {
                            var t = ws.CurrentEditor.ServiceProvider.GetService(typeof(IEditorText)) as IEditorText;
                            t.Text = file.Stream.Decompress<System.Text.UTF8Encoding>();
                        }
                    }
                }
                else
                {
                    _webMatrixHost.ShowNotification("No file history found for: " + ws.CurrentEditor.FilePath);
                }
            }
            else
            {
                _webMatrixHost.ShowNotification("No file is currently open that Autobiography can track history for");
            }
        }

        /// <summary>
        /// Traverses all files in site and creates an entry in the local history for each
        /// </summary>
        /// <param name="parameter">Unused.</param>
        private void SnapshotEntireSite(object parameter)
        {
            if (_ctx == null)
            {
                _webMatrixHost.ShowNotification("Autobiography database not initialized. Please restart WebMatrix");
                return;
            }

            if (_webMatrixHost.WebSite != null && _webMatrixHost.WebSite.Path != null)
            {
                var root = _webMatrixHost.WebSite.Path;
                _webMatrixHost.ShowNotification("Starting Autobiography snapshot for: " + _webMatrixHost.WebSite.Name);
                System.Threading.Tasks.Task.Factory.StartNew(() =>
                {
                    // exclude various extensions and paths - though it may be nice to include images in the future
                    // and provide a restore in place without an open editor instance... hmmm....
                    // var images = new[] { ".gif", ".jpg", ".jpeg", ".png" };
                    var ignore = from path in new[] { @"App_Data\", @"bin\", @".git\", @".hg\" }
                                 select System.IO.Path.Combine(root, path);
                    var files = System.IO.Directory.EnumerateFiles(root, "*", System.IO.SearchOption.AllDirectories)
                                .Where(p => !ignore.Any(i => p.StartsWith(i)));
                                //.Where(p => !images.Any(i => p.EndsWith(i)));

                    foreach (var file in files)
                    {
                        DateTime modified = System.IO.File.GetLastWriteTime(file);
                        int count = _ctx.Files.Where(f => f.FullPath == file && f.Modified == modified).Count();
                        if (count == 0)
                        {
                            AddFileHistory(file);
                        }
                    }
                }).ContinueWith(t => {
                    // how to make this thread safe back on the calling thread?
                    _webMatrixHost.ShowNotification("Snapshot completed for: " + _webMatrixHost.WebSite.Name);
                });
            }
            else
            {
                _webMatrixHost.ShowNotification("No site is currently open that Autobiography can track history for");
            }
        }
    }
}
